#!/usr/bin/python
#coding: utf-8

import sys
sys.path.append("..")

reload(sys)
# 设置编码格式，防止出现UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-14:错误
sys.setdefaultencoding("utf-8")

import random
import time
import numpy as np
import pandas as pd
from datetime import datetime
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
from pandas import Series, DataFrame
import pandas.io.sql as sql
from Config.Config import conn, Select_Sql_Page_Url, Select_Sql_Soft_Info, Select_Sql_Soft_Survey, TimeSleep, Select_Sql_AllInfo2

class AppAnalysis(object):
    def __init__(self):
        self.conn = conn
        self.conn.select_db("appchina")
        self.data_soft_info = sql.read_sql_query(Select_Sql_Soft_Info, conn, index_col= "soft_id")
        self.data_soft_survey = sql.read_sql_query(Select_Sql_Soft_Survey, conn, index_col= "soft_id")
        self.data_page_url = sql.read_sql_query(Select_Sql_Page_Url, conn, index_col= "id")
        self.plt = plt
        self.styles = self.plt.style.available # 获取所有的系统自带显示样式
        # self.all_datas = sql.read_sql_query(Select_Sql_AllInfo2, conn, index_col= 'soft_id')
        # 设置字体
        self.font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
        self.path = r"C:\Users\Administrator\Desktop\Task_Sql\Data\\" # 注意末尾，要用两个\表示一个

        self.Makedirs()

    def Makedirs(self):
        """
        创建data文件夹，用来存数据
        :return:
        """

        import os
        if os.path.exists(self.path[:-2]):
            print u"指定文件夹已经存在"
        else:
            os.makedirs(self.path[:-2])
            print u"创建文件夹成功"

    def ChangeData(self):
        # print self.data_page_url
        # print self.data_soft_info
        # print self.data_soft_survey
        # 把soft_info数据表中字段中多余的部分替换掉
        self.data_soft_info['_update'] = self.data_soft_info['_update'].str.replace(u"更新：", "")
        self.data_soft_info['size'] = self.data_soft_info['size'].str.replace(u"大小：", "")
        self.data_soft_info['edition'] = self.data_soft_info['edition'].str.replace(u"版本：", "")
        self.data_soft_info['classification'] = self.data_soft_info['classification'].str.replace(u"分类：", "")
        self.data_soft_info['requirement'] = (self.data_soft_info['requirement'].str.replace(u"要求：", "")).str.replace(u"以上", "")
        self.data_soft_info['_format'] = self.data_soft_info['_format'].str.replace(u"格式：", "")
        # 通过映射把表示日期的字符串转化为日期格式
        self.data_soft_info['_update'] =self.data_soft_info['_update'].map(lambda x: datetime.strptime(x, "%Y-%m-%d"))

        # 把soft_survey数据表中的comment字段转化为comment_rate，和comment_count两个字段
        self.data_soft_survey.rename(columns= {"comment" : "comment_count"}, inplace= True)
        arr = self.data_soft_survey['comment_count'].str.split(u"好评")
        res1 = []
        res2 = []
        for a in arr:
            res1.append(a[0])
            if len(a[1]) == 0:
                res2.append(0)
            else:
                res2.append(int(a[1].replace('(', "").replace(')', "").replace(u'人', '')))
        self.data_soft_survey['comment_rate'] = Series(res1, index= self.data_soft_survey.index)
        self.data_soft_survey['comment_count'] = Series(res2, index= self.data_soft_survey.index)
        self.data_soft_survey.to_csv(self.path + "data_soft_survey.csv", sep=":")
        print u"写入data_soft_survey.csv文件成功"

        # 获取评论总数量
        print u"评论的数量为：", self.data_soft_survey['comment_count'].sum()

        # # 输出两张数据表的长度
        # print len(self.data_soft_info)
        # print len(self.data_soft_survey)

        # 保存explain字段
        self.SaveExplain()
        # 使用日期时间序列
        self.DrawDateUpdate()
        # 每一个月更新的软件所对应分类数据的柱状图和线形图
        self.DrawClassificationUpdateClassification()
        # 绘制每个季度的软件发布的数量
        self.DrawQuarterUpdate()
        # 绘制分类的图形
        self.DrawClassification()
        # 绘制版本要求的图形
        self.DrawRequirement()
        # 绘制每一季度对应的更新的软件的所对应分类数据的柱状图和线形图
        self.DrawQuarterClassification()
        # 统计每一种分类的评论数量
        self.DrawCommentClassification()
        # 绘制每月发布软件数量的极坐标
        self.DrawSoftCountEveryMonthPolar()
        # 执行plt.show()， 显示图形
        self.PltShow()

    def DrawSoftCountEveryMonthPolar(self):
        """
        根据刚刚存储的data_uc_classification.csv文件，绘制出每个月上传软件的极坐标显示

        Note:
            更多关于极坐标的使用，请参考：
            http://www.cnblogs.com/kallan/p/6738577.html
        :return:
        """
        # 设置头为空，分隔符为分号
        df = pd.read_csv(self.path + "data_uc_classification.csv", header= None, sep= ":")
        data = DataFrame()
        data["month"] = df[0]
        data["numbers"] = df[2]

        # 根据月份进行分组
        data = data.groupby(data.month).sum()

        # 这里末尾又添加了一个开头的数据，是为了形成闭合的图形, 不然绘制不出来极坐标
        index = list(data.index)
        index.append(index[0])  # 修改的是源数据，返回值为None
        number = list(data.numbers)
        number.append(number[0])

        fig = plt.figure(figsize=(10, 10))
        ax = fig.add_subplot(111, projection="polar")   # projection 设置绘制极坐标

        # 把一个圆的弧度分成len(data)等份，并形成闭合图形
        theta = np.linspace(0, 2 * np.pi, len(data), endpoint= False)
        theta = np.append(theta, theta[0])

        ax.plot(theta, number)

        # 设置0度所在的位置
        ax.set_theta_zero_location('N')
        ax.set_xticks(theta)
        ax.set_xticklabels(index, y= 0.02) # y 设置距离y轴的距离
        maxx = int(int(data.max()) / 300)
        # 三元运算符
        maxx = (maxx + 1) * 300 if maxx * 300 < int(data.max()) else maxx * 300
        ax.set_yticks(np.arange(0, maxx, 300))
        # 设置极径显示的最大值
        ax.set_rmax(maxx)
        # position 设置显示title的位置
        ax.set_title(u"每月发布数量", position= (0.5, 1.1), fontproperties= self.font, color= "r", size= 20)

        self.SavePath(u"每月发布数量(极坐标).png")

    def DrawCommentClassification(self):
        """
        统计出来每一种分类的评论数量以及软件数量
        :return:
        """
        # 查看所有的自带样式
        print self.styles
        # 使用自带的样式进行美化
        plt.style.use(self.styles[random.randint(0, len(self.plt.style.available) - 1)])

        self.all_comment_datas = DataFrame()
        self.all_comment_datas['comment_count'] = self.data_soft_survey['comment_count']
        self.all_comment_datas['classification'] = self.data_soft_info['classification']

        # self.all_comment_datas.to_csv("comment_classification.csv")
        self.SavePath(self.all_comment_datas, "comment_classification.csv")
        print u"文件comment_classification.csv写入成功"

        datas = self.all_comment_datas['comment_count'].groupby(self.all_comment_datas.classification).sum()
        # print datas
        fig = plt.figure(figsize=(10, 6))
        ax = fig.add_subplot(1, 1, 1)
        ax.set_xticks(range(len(datas.index)))
        ax.set_xticklabels(list(datas.index), fontproperties= self.font, fontsize= 8)
        datas.plot(ax= ax, alpha= 0.5, label= u"评论数量")
        self.soft_count.plot(ax= ax, alpha= 0.5, label= u"软件数量")
        ax.legend(loc= 0, prop= self.font, fontsize= 8, framealpha= 0.3)
        ax.set_xlabel(u"分类", fontproperties= self.font)
        ax.set_ylabel(u"评论数量", fontproperties= self.font)
        ax.set_title(u"每一种分类的评论数量", fontproperties= self.font)

        ax.grid(True)
        # 保存
        # self.plt.savefig(u"评论and分类.png", dpi=800, bbox_inches="tight")
        self.SavePath(u"评论and分类.png")

    def DrawClassificationUpdateClassification(self):
        """
        绘制每一个月更新的软件所对应分类数据的柱状图和线形图

        Note：
            注意规则同DrawQuarterClassification

        :return:
        """
        # 查看所有的自带样式
        print self.styles
        # 使用自带的样式进行美化
        plt.style.use(self.styles[random.randint(0, len(self.plt.style.available) - 1)])

        self.data_uc_classification = self.data_soft_info.groupby(['update_classification', 'classification']).size()
        # self.data_uc_classification.to_csv("data_uc_classification.csv", sep=":")
        self.SavePath(self.data_uc_classification, "data_uc_classification.csv")
        print u"写入data_uc_classification.csv文件成功"
        self.data_uc_classification = self.data_uc_classification.unstack()
        # print self.data_uc_classification

        fig = plt.figure(figsize= (10, 8))
        ax = fig.add_subplot(1, 1, 1)
        # data.plot(ax= ax, kind= 'bar', stacked= True, alpha= 0.5)
        ax.set_xticks(range(len(self.data_uc_classification.columns)))
        ax.set_xticklabels(list(self.data_uc_classification.columns), fontproperties= self.font, fontsize="small")

        # # 绘制柱状图
        # self.data_uc_classification.plot(ax=ax, kind="bar", stacked=True, alpha=0.5)
        # 绘制线性图
        self.data_uc_classification.plot(ax=ax, alpha=0.5)
        # prop 设置在图例中的显示字体
        ax.legend(self.data_uc_classification.columns, loc=0, prop= self.font, ncol=3, fontsize=10, framealpha= 0.3)
        # 设置标题和轴标签，如果要在轴标签和标题上面显示中文，需要设置字体
        ax.set_xlabel(u"月份", fontproperties= self.font)
        ax.set_ylabel(u"每种软件的柱状图显示", fontproperties= self.font)
        # ax.set_title(u"每一月份更新的软件的所对应分类数据的柱状图", fontproperties= self.font)
        ax.set_title(u"每一月份更新的软件的所对应分类数据的线性图", fontproperties= self.font)

        ax.grid(True)

        # 保存
        # self.plt.savefig(u"月份and分类柱状图.png", dpi=800, bbox_inches="tight")
        # self.plt.savefig(u"月份and分类线性图.png", dpi=800, bbox_inches="tight")
        # self.SavePath(u"月份and分类柱状图.png")
        self.SavePath(u"月份and分类线性图.png")

    def DrawQuarterClassification(self):
        """
        绘制每一季度更新的软件的所对应分类数据的柱状图和线形图

        Note:
            必须要同时运行DrawQuarterUpdate，因为quarter列是在DrawQuarterUpdate中创建的。
            而且DrawQuarterUpdate的运行要在DrawQuarterClassification之前。
            这可以说是一个bug，应该把数据插入到数据库或者csv中，这样可以减少执行这一步的时间消耗。
            **这里已经把数据写入到csv文件中了，可以通过读取csv文件的方式，获取到data_soft_info中的数据，
            解除两个类方法必须同时运行的关联。

            把柱状图修改为线形图的时候记得要修改3个地方

            虽然要绘制2张图表，但是并没有选择在一张上面绘制，因为在一张上面绘制的时候每一张图片显示的都是占一张图片的1/2，
            显示效果不好

        BUG:
            在绘制 季度and分类线性图 的时候。显示的有些问题，没有找到原因，不过并不是错误，不再深究
        :return:
        """
        print self.styles
        plt.style.use(self.styles[random.randint(0, len(self.plt.style.available) - 1)])

        self.data_quarter_classification = self.data_soft_info.groupby(['quarter', 'classification']).size()
        # self.data_quarter_classification.to_csv("data_quarter_classification.csv", sep=":")
        self.SavePath(self.data_quarter_classification, "data_quarter_classification.csv")
        print u"写入data_quarter_classification.csv文件成功"
        # print self.data_quarter_classification

        # 把内层的行转化为列
        self.data_quarter_classification = self.data_quarter_classification.unstack()
        # print self.data_quarter_classification

        # 设置长宽大小为(7, 7)主要是因为显示柱状图的时候太宽了，设置的size小的时候至少显示的还可以看
        fig = plt.figure(figsize= (10, 7))
        ax = fig.add_subplot(1, 1, 1)
        ax.set_xticks(range(len(self.data_quarter_classification.index)))
        ax.set_xticklabels(self.data_quarter_classification.index, fontproperties= self.font, fontsize= "small")

        # 修改
        # # 绘制柱状图
        # self.data_quarter_classification.plot(ax=ax, kind="bar", stacked=True, alpha=0.5)
        # 绘制线性图
        self.data_quarter_classification.plot(ax=ax, alpha=0.5)

        # prop 设置在图例中的显示字体
        ax.legend(self.data_quarter_classification.columns, loc=0, prop= self.font, ncol=3, fontsize=6, framealpha= 0.3)
        ax.set_xlabel(u"季度", fontproperties= self.font)
        ax.set_ylabel(u"每种软件的柱状图显示", fontproperties= self.font)

        # 修改
        # ax.set_title(u"每一季度更新的软件的所对应分类数据的柱状图", fontproperties= self.font)
        ax.set_title(u"每一季度更新的软件的所对应分类数据的线性图", fontproperties= self.font)

        ax.grid(True)

        # 修改
        # self.plt.savefig(u"季度and分类柱状图.png", dpi=800, bbox_inches="tight")
        # self.plt.savefig(u"季度and分类线性图.png", dpi=800, bbox_inches="tight")
        # self.SavePath(u"季度and分类柱状图.png")
        self.SavePath(u"季度and分类线性图.png")

    def DrawQuarterUpdate(self):
        """
        根据季度绘制每一个季度有多少软件发布

        关于季度：
            在这里设置的是每年的1-3月为第一季度，依次类推。
            当然也可以设置为其它形式的，因具体要求而异即可。
        :return:
        """

        print self.styles
        plt.style.use("ggplot")

        self.data_soft_info['quarter'] = self.data_soft_info['_update'].map(
            lambda x: pd.Period(x, 'Q-DEC'))

        self.data_quarter = self.data_soft_info.groupby(self.data_soft_info['quarter']).size()
        self.data_quarter.sort_index(ascending= True, inplace= True)  # 设置日期从前往后
        # self.data_quarter.to_csv("data_quarter.csv", sep=":")
        self.SavePath(self.data_quarter, "data_quarter.csv")
        print u"写入data_quarter.csv文件成功"
        # print self.data_update

        fig = self.plt.figure(figsize=(10, 6))
        ax = fig.add_subplot(1, 1, 1)

        ax.set_xticks(range(len(self.data_quarter)))
        ax.set_xticklabels(self.data_quarter.index, fontproperties= self.font)
        ax.set_xlabel(u"对应季度", fontproperties= self.font)
        ax.set_ylabel(u"每个季度更新的软件数量", fontproperties= self.font)
        ax.set_title(u"根据季度对软件进行划分(1-3月为第一季度)", fontproperties= self.font)

        ax.grid(True)

        ax.plot(list(self.data_quarter.values))

        # self.plt.savefig(u"季度划分.png", dpi=800, bbox_inches="tight")
        self.SavePath(u"季度划分.png")

    def DrawDateUpdate(self):
        """
        新添加一个字段：每一年的月份，接下来根据此字段绘制每一月的软件数量的图片
        :return:
        """

        print self.styles
        plt.style.use(self.styles[random.randint(0, len(self.plt.style.available) - 1)])

        self.data_soft_info['update_classification'] = self.data_soft_info['_update'].map(lambda x : "-".join(str(x).split("-")[: -1]))
        # self.data_soft_info.to_csv("data_soft_info.csv", sep=":")
        self.SavePath(self.data_soft_info, "data_soft_info.csv")
        print u"写入data_soft_info.csv文件成功"
        self.data_update = self.data_soft_info.groupby(self.data_soft_info['update_classification']).size()
        self.data_update.sort_index(ascending= True, inplace= True) # 设置日期从前往后
        # self.data_update.to_csv("soft_update_classification.csv", sep=":")
        self.SavePath(self.data_update, "soft_update_classification.csv")
        print u"写入soft_update_classification.csv文件成功"
        # print self.data_update

        fig = self.plt.figure(figsize= (10, 6))
        ax = fig.add_subplot(1, 1, 1)

        ax.set_xticks(range(len(self.data_update)))
        ax.set_xticklabels(self.data_update.index, fontproperties= self.font)
        # 设置标题和轴标签，如果要在轴标签和标题上面显示中文，需要设置字体
        ax.set_xlabel(u"发布月份", fontproperties= self.font)
        ax.set_ylabel(u"发布数量", fontproperties= self.font)
        ax.set_title(u"每月发布数量", fontproperties= self.font)

        ax.grid(True)

        ax.plot(list(self.data_update.values))

        # self.plt.savefig(u"每月更新.png", dpi=800, bbox_inches="tight")
        self.SavePath(u"每月更新.png")

    def DrawRequirement(self):
        """
        根据版本信息获取到可在相应版本中使用的数量

        Note:
            软件版本有个自适应的功能，比如说：软件可以在Android1.0中使用，则一定可以在Android2.0中使用，所以这里还要再加一个累计和
        :return:
        """
        print self.styles
        plt.style.use("ggplot")

        self.data_requirement = self.data_soft_info.groupby(self.data_soft_info['requirement']).size()
        self.data_requirement.sort_index(ascending= True, inplace= True)

        # 加入累计和，若要不带累计和，把这一行注释即可
        self.data_requirement = self.data_requirement.cumsum()

        # self.data_requirement.to_csv("soft_requirement.csv", sep=":")
        self.SavePath(self.data_requirement, "soft_requirement.csv")
        print u"写入soft_requirement.csv文件成功"

        # print self.data_requirement

        fig = self.plt.figure(figsize= (10, 6))
        ax = fig.add_subplot(1, 1, 1)

        ax.set_xticks(range(len(self.data_requirement.index)))
        ax.set_xticklabels(list(self.data_requirement.index), fontproperties= self.font, fontsize= "small")
        ax.set_xlabel(u"版本要求", fontproperties= self.font)
        ax.set_ylabel(u"相关版本的应用数量", fontproperties= self.font)
        ax.set_title(u"Android系统版本要求", fontproperties= self.font)

        ax.grid(True)

        # 绘图，根据每一个分类的数量
        ax.plot(list(self.data_requirement.values))

        # self.plt.savefig(u"版本要求(不带累计和).png", dpi= 800, bbox_inches= "tight")
        # self.plt.savefig(u"版本要求(带累计和).png", dpi= 800, bbox_inches= "tight")
        # self.SavePath(u"版本要求(不带累计和).png")
        self.SavePath(u"版本要求(带累计和).png")

    def DrawClassification(self):
        """
        根据分类列绘制出每一个分类的所占软件的数量
        :return:
        """

        # 查看所有的自带样式
        print self.styles
        # 使用自带的样式进行美化
        plt.style.use(self.styles[random.randint(0, len(self.plt.style.available) - 1)])

        self.soft_count = self.data_soft_info.groupby(self.data_soft_info['classification']).size()
        # self.soft_count.to_csv("soft_classification_count.csv", sep=":")
        self.SavePath(self.soft_count, "soft_classification_count.csv")
        print u"写入soft_classification_count.csv文件成功"

        fig = plt.figure(figsize=(10, 6))
        ax = fig.add_subplot(1, 1, 1)

        # 设置刻度以及刻度标签
        ax.set_xticks(range(len(self.soft_count.index)))
        ax.set_xticklabels(list(self.soft_count.index), fontproperties=self.font, fontsize= 8)
        # 设置标题和轴标签
        ax.set_xlabel(u"分类", fontproperties= self.font)
        ax.set_ylabel(u"每个分类对应应用的数量", fontproperties= self.font)
        ax.set_title(u"应用分类数据显示", fontproperties= self.font)

        # True 显示网格 ,可以设置的参数如下
        # linestyle 设置线显示的类型(一共四种)
        # color 设置网格的颜色
        # linewidth 设置网格的宽度
        ax.grid(True)

        # 绘图，根据每一个分类的数量
        ax.plot(list(self.soft_count.values))

        # 保存
        # self.plt.savefig(u"分类.png", dpi=400, bbox_inches="tight")
        self.SavePath(u"分类.png")

    def SaveExplain(self):
        self.soft_explain = self.data_soft_survey.groupby(self.data_soft_survey['exp']).size()
        # 求平均值
        self.soft_explain_mean = int(self.soft_explain.mean())
        # 对soft_explain进行降序排序，在原soft_explain上面进行就地修改
        self.soft_explain.sort_values(ascending=False, inplace=True)
        # print self.soft_explain
        # self.soft_explain.to_csv("explains.csv", sep=":")
        self.SavePath(self.soft_explain, "explains.csv")
        print u"写入soft_explain.csv文件成功"

    def SavePath(self, *args):
        if len(args) == 1:  # 此时为保存图片
            self.plt.savefig(self.path + args[0], dpi=400, bbox_inches="tight")
        else:   # 保存csv文件
            args[0].to_csv(self.path + args[1], sep= ":")

    def PltShow(self):
        self.plt.show()

def DataAnalaysisMain():
    import time
    t1 = time.time()
    print u"第六步开始前的时间为", time.ctime(time.time())
    app = AppAnalysis()
    app.ChangeData()
    t2 = time.time()
    print u"第六步结束时的时间为", time.ctime(time.time())
    print u"总花费时间为", time.time()

if __name__ == "__main__":
    DataAnalaysisMain()